﻿// <copyright file="ContosoAnalytics.cs" company="Microsoft Corporation">
// Copyright (c) 2009 Microsoft Corporation All Rights Reserved
// </copyright>
// <author>Michael S. Scherotter</author>
// <email>mischero@microsoft.com</email>
// <date>2009-06-04</date>
// <summary>ContosoAnalytics class definition</summary>

namespace Contoso.WebAnalytics
{
    using System;
    using System.ComponentModel.Composition;
    using System.Globalization;
    using System.IO;
    using System.IO.IsolatedStorage;
    using System.Net;
    using System.Runtime.Serialization;
    using System.Text;
    using System.Windows;
    using System.Windows.Browser;
    using System.Windows.Controls;
    using System.Windows.Interactivity;
    using System.Windows.Media.Imaging;
    using Microsoft.WebAnalytics;

    /// <summary>
    /// HTTP Request type
    /// </summary>
    public enum RequestType
    {
        /// <summary>
        /// GIF Image Request (HTTP Get with data in URL)
        /// </summary>
        GifImage,

        /// <summary>
        /// HTTP Get method (data in URL)
        /// </summary>
        Get,

        /// <summary>
        /// HTTP Post method (data in body)
        /// </summary>
        Post
    }

    /// <summary>
    /// Analytics Service for Contoso Analytics (a Ficticous Web Analytics Service)
    /// </summary>
    /// <remarks>This is a sample vendor component for the Silverlight Analytics Framework</remarks>
    public class ContosoAnalytics : Behavior<Panel>
    {
        #region Fields
        /// <summary>
        /// The Cookie Name
        /// </summary>
        private const string CookieName = "Contoso.WebAnalytics.UserId";

        /// <summary>
        /// The native image
        /// </summary>
        private Image image;

        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the ContosoAnalytics class.
        /// </summary>
        public ContosoAnalytics()
        {
            this.RequestType = RequestType.GifImage;
        }
        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the category for the group of objects you want to track.
        /// </summary>
        public string Category { get; set; }

        /// <summary>
        /// Gets or sets the Contoso Analyitcs Profile Id
        /// </summary>
        public string ProfileId { get; set; }

        /// <summary>
        /// Gets or sets the request type
        /// </summary>
        public RequestType RequestType { get; set; }

        /// <summary>
        /// Gets or sets the Web Analytics events
        /// </summary>
        [Import]
        public WebAnalyticsEvents Events { get; set; }

        /// <summary>
        /// Gets or sets the web message dispatcher
        /// </summary>
        [Import]
        public Action<WebMessage> Dispatch { get; set; }

        /// <summary>
        /// Gets or sets the user cookie
        /// </summary>
        private string Cookie { get; set; }

        #endregion

        #region Methods

        /// <summary>
        /// This is enabled if on an HTML Page
        /// </summary>
        /// <exception cref="InvalidOperationException">if the Contoso Analytics page tag is not on the page</exception>
        protected override void OnAttached()
        {
            base.OnAttached();

            try
            {
                if (IsolatedStorageSettings.ApplicationSettings.Contains(CookieName))
                {
                    this.Cookie = IsolatedStorageSettings.ApplicationSettings[CookieName] as string;
                }
                else
                {
                    this.Cookie = Guid.NewGuid().ToString();

                    IsolatedStorageSettings.ApplicationSettings.Add(CookieName, this.Cookie);
                }
            }
            catch (IsolatedStorageException)
            {
                // Application Storage is Disabled
                this.Cookie = Guid.NewGuid().ToString();
            }

            CompositionInitializerEx.SatisfyImports(this);

            this.Events.TrackEvent += new EventHandler<AnalyticsEventArgs>(this.OnTrackEvent);

            this.AddImage();

            this.AssociatedObject.Unloaded += new RoutedEventHandler(delegate(object sender, RoutedEventArgs args)
            {
                System.Windows.Interactivity.Interaction.GetBehaviors(this.AssociatedObject).Remove(this);
            });
        }

        /// <summary>
        /// Remove the page tag
        /// </summary>
        protected override void OnDetaching()
        {
            base.OnDetaching();

            this.Events.TrackEvent -= new EventHandler<AnalyticsEventArgs>(this.OnTrackEvent);

            if (this.image != null)
            {
                this.AssociatedObject.Children.Remove(this.image);

                this.image = null;
            }
        }

        /// <summary>
        /// The image will always fail because the Silverlight does not read GIF images
        /// </summary>
        /// <param name="sender">the page tag image</param>
        /// <param name="e">the exception routed event arguments</param>
        private void OnImageFailed(object sender, ExceptionRoutedEventArgs e)
        {
        }

        /// <summary>
        /// Add the image to the associated object
        /// </summary>
        private void AddImage()
        {
            this.image = new Image();

            this.image.ImageFailed += new EventHandler<ExceptionRoutedEventArgs>(this.OnImageFailed);

            this.AssociatedObject.Children.Add(this.image);
        }

        /// <summary>
        /// If Native tracking, then format a URL for the image, otherwise format a URL for the JavaScript API
        /// </summary>
        /// <param name="sender">the Data collector</param>
        /// <param name="e">the analytics event arguments</param>
        private void OnTrackEvent(object sender, AnalyticsEventArgs e)
        {
            switch (this.RequestType)
            {
                case RequestType.GifImage:
                    {
                        var url = this.BuildGifUrl(e.AnalyticsEvent.Name, e.AnalyticsEvent.ObjectName, e.AnalyticsEvent.Value, e.AnalyticsEvent.StateName);

                        this.image.Source = new BitmapImage(url);
                    }

                    break;

                case RequestType.Get:
                    {
                        var uri = this.BuildGetUrl(e.AnalyticsEvent.Name, e.AnalyticsEvent.ObjectName, e.AnalyticsEvent.Value, e.AnalyticsEvent.StateName);

                        this.Dispatch(new WebMessage()
                        {
                            Address = uri,
                            Method = "GET",
                        });

                        ////var client = new WebClient();

                        ////client.DownloadStringAsync(uri);
                    }

                    break;

                case RequestType.Post:
                    {
                        var uri = new Uri("http://www.contoso.com/analytics.svc");

                        var serializer = new DataContractSerializer(typeof(AnalyticsEvent));

                        using (MemoryStream stream = new MemoryStream())
                        {
                            serializer.WriteObject(stream, e.AnalyticsEvent);

                            stream.Flush();

                            stream.Position = 0;

                            using (var reader = new StreamReader(stream))
                            {
                                var data = reader.ReadToEnd();

                                this.Dispatch(new WebMessage()
                                {
                                    Address = uri,
                                    Content = data,
                                    Method = "POST"
                                });
                            }
                        }
                    }

                    break;
            }
        }

        /// <summary>
        /// Build a URI for a HTTP Get request
        /// </summary>
        /// <param name="eventName">the event name</param>
        /// <param name="objectName">the object name</param>
        /// <param name="value">an optional numeric value</param>
        /// <param name="stateName">the state name</param>
        /// <returns>the HTTP Get URI</returns>
        private Uri BuildGetUrl(string eventName, string objectName, int? value, string stateName)
        {
            StringBuilder builder = new StringBuilder("http://www.contoso.com/analytics.svc?");

            builder.AppendFormat(CultureInfo.InvariantCulture, "u={0}", this.Cookie);

            builder.AppendFormat(CultureInfo.InvariantCulture, "&h={0}", DateTime.Now.GetHashCode());

            builder.AppendFormat(CultureInfo.InvariantCulture, "&ho={0}", HtmlPage.Document.DocumentUri.Host);

            builder.AppendFormat(CultureInfo.InvariantCulture, "&e={0}", eventName);

            builder.AppendFormat(CultureInfo.InvariantCulture, "&l={0}", objectName);

            builder.AppendFormat(CultureInfo.InvariantCulture, "&p={0}", this.ProfileId);

            if (value.HasValue)
            {
                builder.AppendFormat(CultureInfo.InvariantCulture, "&v={0}", value.Value);
            }

            if (!string.IsNullOrEmpty(stateName))
            {
                builder.AppendFormat(CultureInfo.InvariantCulture, "&s={0}", stateName);
            }

            return new Uri(builder.ToString(), UriKind.Absolute);
        }

        /// <summary>
        /// Build a URL for a GIF image request to Contoso analytics
        /// </summary>
        /// <param name="action">the action or event name</param>
        /// <param name="label">a label to use</param>
        /// <param name="value">an optional value</param>
        /// <param name="stateName">an optional state name</param>
        /// <returns>the URL to the Contoso Analytics GIF image</returns>
        /// <remarks>we don't use utmje or utmfl because this can be running out of the browser</remarks>
        private Uri BuildGifUrl(string action, string label, int? value, string stateName)
        {
            StringBuilder builder = new StringBuilder("http://www.contoso.com/analytics.gif?");

            builder.AppendFormat(CultureInfo.InvariantCulture, "u={0}", this.Cookie);

            builder.AppendFormat(CultureInfo.InvariantCulture, "&h={0}", DateTime.Now.GetHashCode());

            builder.AppendFormat(CultureInfo.InvariantCulture, "&ho={0}", HtmlPage.Document.DocumentUri.Host);

            builder.AppendFormat(CultureInfo.InvariantCulture, "&e={0}", action);

            builder.AppendFormat(CultureInfo.InvariantCulture, "&l={0}", label);

            builder.AppendFormat(CultureInfo.InvariantCulture, "&p={0}", this.ProfileId);

            if (value.HasValue)
            {
                builder.AppendFormat(CultureInfo.InvariantCulture, "&v={0}", value.Value);
            }

            if (!string.IsNullOrEmpty(stateName))
            {
                builder.AppendFormat(CultureInfo.InvariantCulture, "&s={0}", stateName);
            }

            return new Uri(builder.ToString(), UriKind.Absolute);
        }

        #endregion
    }
}